package com.weilele.mvvm.utils

import android.animation.Animator
import android.animation.ValueAnimator
import android.content.Context
import android.content.Intent
import android.content.pm.PackageManager
import android.net.Uri
import android.os.Build
import android.os.Environment
import android.provider.MediaStore
import android.text.format.DateUtils
import android.util.Base64
import android.view.ViewPropertyAnimator
import androidx.appcompat.app.AppCompatActivity
import androidx.core.content.FileProvider
import androidx.core.text.isDigitsOnly
import com.weilele.mvvm.app
import com.weilele.mvvm.base.helper.ignoreError
import com.weilele.mvvm.logI
import com.weilele.mvvm.utils.file.DownloadToOutputStream
import com.weilele.mvvm.utils.file.SaveFileType
import com.weilele.mvvm.utils.file.insetFileToContentResolver
import kotlinx.coroutines.CoroutineScope
import java.io.*
import java.math.BigDecimal
import java.math.RoundingMode
import java.security.MessageDigest
import java.security.NoSuchAlgorithmException
import java.text.SimpleDateFormat
import java.util.*
import javax.crypto.Cipher
import javax.crypto.spec.SecretKeySpec
import kotlin.collections.HashMap
import kotlin.math.abs
import kotlin.math.acos
import kotlin.math.cos
import kotlin.math.sin


/**
 * 描述：其他工具类集合
 */
/**
 * 通用的错误捕获，默认不处理错误，只是防止崩溃
 */
fun tryError(
        handlerError: ((Throwable) -> Unit)? = null,
        finally: (() -> Unit)? = null,
        code: () -> Unit
) {
    try {
        code.invoke()
    } catch (e: Throwable) {
        e.printStackTrace()
        handlerError?.invoke(e)
    } finally {
        finally?.invoke()
    }
}

/**
 * 毫秒换成00:00:00
 */
//一小时的毫秒数
const val ONE_HOUR_OF_MILLISECOND = 1 * 60 * 60 * 1000L

/**
 *
 * Formats an elapsed time in the form "MM:SS" or "H:MM:SS"
 * for display on the call-in-progress screen.
 * @param elapsedSeconds the elapsed time in seconds.
 * DateUtils:安卓提供的工具类
 */
fun formatElapsedTime(elapsedSeconds: Long): String {
    return DateUtils.formatElapsedTime(elapsedSeconds)
}

/**
 * 毫秒换成00:00:00
 */
fun getCountTimeByLong(finishTime: Long, isNeedHour: Boolean = finishTime > ONE_HOUR_OF_MILLISECOND/*大于1小时则显示*/): String {
    var totalTime = (finishTime / 1000).toInt()//秒
    var hour = 0
    var minute = 0
    var second = 0
    if (3600 <= totalTime) {
        hour = totalTime / 3600
        totalTime -= 3600 * hour
    }
    if (60 <= totalTime) {
        minute = totalTime / 60
        totalTime -= 60 * minute
    }
    if (0 <= totalTime) {
        second = totalTime
    }
    val sb = StringBuilder()
    if (isNeedHour) {
        if (hour < 10) {
            sb.append("0").append(hour).append(":")
        } else {
            sb.append(hour).append(":")
        }
    }
    if (minute < 10) {
        sb.append("0").append(minute).append(":")
    } else {
        sb.append(minute).append(":")
    }
    if (second < 10) {
        sb.append("0").append(second)
    } else {
        sb.append(second)
    }
    return sb.toString()
}


/**时间戳转日期*/
fun longTimeToStr(time: Long?, pattern: String = "yyyy-MM-dd HH:mm"): String {
    if (time == null) {
        return ""
    }
    val format = SimpleDateFormat(pattern, Locale.getDefault())
    return format.format(time).toString()
}

/**年月日转时间戳*/
fun ymdToLongTime(year: Int, month: Int, day: Int, isFirst: Boolean): Long {
    val calendar = GregorianCalendar()
    if (isFirst) {
        calendar.set(year, month - 1, day, 0, 0)
    } else {
        calendar.set(year, month - 1, day, 23, 59)
    }
    return calendar.timeInMillis
}

/**年月日转时间戳*/
fun ymdHmToLongTime(year: Int, month: Int, day: Int, hourOfDay: Int, minute: Int): Long {
    val calendar = GregorianCalendar()
    calendar.set(year, month - 1, day, hourOfDay, minute)
    return calendar.timeInMillis
}

/**
 * 保留两位小数
 */
fun BigDecimal?.keepTwoPoint(roundingMode: RoundingMode = RoundingMode.HALF_EVEN): String {
    return try {
        this?.setScale(2, roundingMode)?.toString() ?: "0.00"
    } catch (e: Throwable) {
        "0.00"
    }
}

fun BigDecimal?.keepDecimal(
        count: Int,
        roundingMode: RoundingMode = RoundingMode.HALF_EVEN
): BigDecimal? {
    return try {
        this?.setScale(count, roundingMode)
    } catch (e: Throwable) {
        e.printStackTrace()
        this
    }
}

/**
 * 保留两位小数
 */
fun Double?.keepTwoPoint(roundingMode: RoundingMode = RoundingMode.HALF_EVEN): String {
    this ?: return "0.00"
    return this.toString().keepTwoPoint()
}

/**
 * 保留两位小数
 */
fun Float?.keepTwoPoint(roundingMode: RoundingMode = RoundingMode.HALF_EVEN): String {
    this ?: return "0.00"
    return this.toString().keepTwoPoint()
}

/**
 * 保留两位小数
 */
fun String?.keepTwoPoint(roundingMode: RoundingMode = RoundingMode.HALF_EVEN): String {
    return try {
        if (this == null) {
            "0.00"
        } else {
            BigDecimal(this).setScale(2, roundingMode)?.toString() ?: this
        }
    } catch (e: Throwable) {
        "0.00"
    }
}

/**
 * 毫秒四舍五入转为秒
 */
fun millisecondToSecond(millisecond: Long): Long {
    return BigDecimal(millisecond / 1000.0).setScale(0, RoundingMode.HALF_EVEN).toLong()
}

/**
 * 判断某个对象是否是这个类型
 */
inline fun <reified Obj> Any?.isThis(isThis: Obj.() -> Unit) {
    if (this is Obj) {
        isThis(this)
    }
}

/**
 * 安全的类型转换
 */
inline fun <reified Obj> Any?.safeGet(): Obj? {
    return if (this is Obj) {
        this
    } else {
        null
    }
}

/**
 * 获取openSsl haskKey
 * 比如facebook的login需要这个
 */
fun Context.getAppKeyHash(): MutableList<String> {
    val keyHashList = mutableListOf<String>()
    try {
        val signatures = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
            packageManager.getPackageInfo(packageName, PackageManager.GET_SIGNING_CERTIFICATES).signingInfo.apkContentsSigners
        } else {
            packageManager.getPackageInfo(packageName, PackageManager.GET_SIGNATURES).signatures
        }
        signatures?.forEach { signature ->
            val md = MessageDigest.getInstance("SHA1")
            md.update(signature.toByteArray())
            val keyHash =
                    Base64.encodeToString(md.digest(), Base64.DEFAULT)
            keyHashList.add(keyHash)
        }
    } catch (error: Throwable) {
        error.printStackTrace()
    } finally {
        return keyHashList
    }
}

/**
 *获取签名的sha1
 * 比如百度地图就需要这个
 */
fun Context.getAppSHA1(): MutableList<String>? {
    return getMessageDigest("SHA1")
}

/**
 *获取签名的sha256
 */
fun Context.getAppSHA256(): MutableList<String>? {
    return getMessageDigest("SHA256")
}

fun Context.getMessageDigest(algorithm: String): MutableList<String>? {
    val shals = mutableListOf<String>()
    try {
        val signatures = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
            packageManager.getPackageInfo(packageName, PackageManager.GET_SIGNING_CERTIFICATES).signingInfo.apkContentsSigners
        } else {
            packageManager.getPackageInfo(packageName, PackageManager.GET_SIGNATURES).signatures
        }
        signatures?.forEach {
            val cert: ByteArray? = it.toByteArray()
            val md: MessageDigest = MessageDigest.getInstance(algorithm)
            val publicKey: ByteArray = md.digest(cert)
            val hexString = StringBuffer()
            for (i in publicKey.indices) {
                val appendString = Integer.toHexString(0xFF and publicKey[i].toInt())
                        .toUpperCase(Locale.US)
                if (appendString.length == 1) hexString.append("0")
                hexString.append(appendString)
                hexString.append(":")
            }
            val result = hexString.toString()
            val sha1 = result.substring(0, result.length - 1)
            shals.add(sha1)
        }
    } catch (e: Throwable) {
        e.printStackTrace()
    } finally {
        return shals
    }
}


/**
 * 比较两个字符串的相识度
 * 核心算法：用一个二维数组记录每个字符串是否相同，如果相同记为0，不相同记为1，每行每列相同个数累加
 * 则数组最后一个数为不相同的总数，从而判断这两个字符的相识度
 */
private fun compare(str: String, target: String): Int {
    val d: Array<IntArray> // 矩阵
    val n = str.length
    val m = target.length
    var i = 0 // 遍历str的
    var j: Int // 遍历target的
    var ch1: Char // str的
    var ch2: Char // target的
    var temp: Int // 记录相同字符,在某个矩阵位置值的增量,不是0就是1
    if (n == 0) {
        return m
    }
    if (m == 0) {
        return n
    }
    d = Array(n + 1) { IntArray(m + 1) }
    // 初始化第一列
    while (i <= n) {
        d[i][0] = i
        i++
    }
    // 初始化第一行
    j = 0
    while (j <= m) {
        d[0][j] = j
        j++
    }
    i = 1
    while (i <= n) {
        // 遍历str
        ch1 = str[i - 1]
        // 去匹配target
        j = 1
        while (j <= m) {
            ch2 = target[j - 1]
            temp =
                    if (ch1 == ch2 || ch1.toInt() == ch2.toInt() + 32 || ch1.toInt() + 32 == ch2.toInt()) {
                        0
                    } else {
                        1
                    }
            // 左边+1,上边+1, 左上角+temp取最小
            d[i][j] = min(d[i - 1][j] + 1, d[i][j - 1] + 1, d[i - 1][j - 1] + temp)
            j++
        }
        i++
    }
    return d[n][m]
}


/**
 * 获取最小的值
 */
private fun min(oldOne: Int, two: Int, three: Int): Int {
    var one = oldOne
    return if ((if (one < two) one else two.also { one = it }) < three) one else three
}


/**
 * 获取两字符串的相似度
 */
fun getStringSimilarityRatio(str: String, target: String): Float {
    val max = str.length.coerceAtLeast(target.length)
    return 1 - compare(str, target).toFloat() / max
}

/**
 * 安装App
 */
fun Context?.installApk(oldFile: File?) {
    oldFile ?: return
    this ?: return
    val file = File(getExternalFilesDir(Environment.DIRECTORY_DOWNLOADS)?.path + "/temp.apk")
    if (!file.exists()) {
        file.createNewFile()
    }
    file.writeBytes(oldFile.readBytes())
    val intent = Intent(Intent.ACTION_VIEW)
    intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
    //判读版本是否在7.0以上
    if (Build.VERSION.SDK_INT >= 24) {
        val apkUri =
                FileProvider.getUriForFile(this, "${packageName}.fileprovider", file)
        intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
        intent.setDataAndType(apkUri, "application/vnd.android.package-archive")
    } else {
        intent.setDataAndType(Uri.fromFile(file), "application/vnd.android.package-archive")
    }
    startActivity(intent)
}

/**
 * 安装App
 */
fun AppCompatActivity?.installApk(uri: Uri?) {
    uri ?: return
    this ?: return
    val intent = Intent(Intent.ACTION_VIEW)
    intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
    //判读版本是否在7.0以上
    if (Build.VERSION.SDK_INT >= 24) {
        intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
        intent.setDataAndType(uri, "application/vnd.android.package-archive")
    } else {
        intent.setDataAndType(uri, "application/vnd.android.package-archive")
    }
    startActivity(intent)
}

/**
 * 下载并安装apk
 * 文件直接下载在公共文件夹，省去复制操作
 */
fun AppCompatActivity?.downloadAndInstallApk(
        scope: CoroutineScope,
        url: String,
        displayName: String,
        listener: Function2<Uri, DownloadToOutputStream, Unit>? = null
) {
    this ?: return
    val uri = insetFileToContentResolver(
            SaveFileType.Download,
            "application/apk",
            displayName,
            SaveFileType.Download.path,
            null
    )
            ?: return
    //若生成了uri，则表示该文件添加成功
    //使用流将内容写入该uri中即可
    val output = app.contentResolver.openOutputStream(uri) ?: return
    val helper = DownloadToOutputStream(scope, url, output)
    listener?.invoke(uri, helper)
    helper.start()
    helper.setOnCompleteListener {
        installApk(uri)
    }
}

fun Context?.getFileFromContentUri(contentUri: Uri?): File? {
    this ?: return null
    contentUri ?: return null
    var file: File? = null
    var filePath: String? = null
    var fileName: String? = null
    val contentResolver = contentResolver;
    val filePathColumn = arrayOf(MediaStore.MediaColumns.DATA, MediaStore.MediaColumns.DISPLAY_NAME)
    val cursor = contentResolver.query(
            contentUri, filePathColumn, null,
            null, null
    );
    if (cursor != null) {
        cursor.moveToFirst()
        filePath = cursor.getString(cursor.getColumnIndex(filePathColumn[0]));
        fileName = cursor.getString(cursor.getColumnIndex(filePathColumn[1]));
        cursor.close()
        filePath?.let {
            file = File(it)
        }
    }
    return file;
}

/**
 * 如果数字大于1万，怎用万作为单位
 */
fun String?.tenThousandFormat(text: String = "万", decimalCount: Int = 1): String {
    this ?: return ""
    if (!this.isDigitsOnly()) {
        return this
    }
    val doubleValue = this.toDouble()
    val max = 10000.0
    if (doubleValue < max) {
        return this
    }
    val format = BigDecimal.valueOf(doubleValue / max).keepDecimal(decimalCount)?.toString() ?: ""
    return "${format}$text"
}

/**
 *
 * 字符串美化
 *  1234->1，234
 *  1234->123,4
 *  123456->123,456
 */
fun String?.insetSplitStr(
        splitCount: Int = 3/*多少字符之后插入新字符*/,
        splitStr: String = ","/*插入新字符*/,
        startFromEnd: Boolean = true/*是否从末尾开始计算插入*/
): String {
    this ?: return ""
    val numStr = StringBuilder(this)
    val count = numStr.count()
    val n1 = count % splitCount
    val n2 = count / splitCount
    var insetCount = 0
    var insetOff = 0
    val splitStrCount = splitStr.count()
    if (startFromEnd) {
        repeat(n2) {
            when {
                n1 == 0 && it == 0 -> {
                    //这种情况，不做处理
                    // 否则出现 123456->,123,456
                }
                else -> {
                    numStr.insert(n1 + it * splitCount + insetOff, splitStr)
                    insetCount++
                    insetOff = insetCount * splitStrCount
                }
            }
        }
    } else {
        repeat(n2) {
            when {
                n1 == 0 && it == n2 - 1 -> {
                    //这种情况，不做处理
                    // 否则出现 123456->123,456,
                }
                else -> {
                    numStr.insert(splitCount + it * splitCount + insetOff, splitStr)
                    insetCount++
                    insetOff = insetCount * splitStrCount
                }
            }
        }
    }
    return numStr.toString()
}

private val EARTHS_RADIUS = doubleArrayOf(
        6378.1,  // Kilometers
        3963.1676,  // Statue miles
        3443.89849 // Nautical miles
)

/**
 * 根据经纬度计算两个点的距离
 * 单位返回的m还是km待验证
 */
fun getDistanceByLatLng(
        startLatitude: Double,
        startLongitude: Double,
        endLatitude: Double,
        endLongitude: Double
): Double {
    val lat1 = Math.PI / 180 * startLatitude
    val lat2 = Math.PI / 180 * endLatitude
    val lon1 = Math.PI / 180 * startLongitude
    val lon2 = Math.PI / 180 * endLongitude
    val earthRadius: Double = EARTHS_RADIUS[0] //6371;//Radius of the earth
    val d = (acos(
            sin(lat1) * sin(lat2) + (cos(lat1) * cos(lat2)
                    * cos(lon2 - lon1))
    )
            * earthRadius)
    return d * 1000
}

/**
 * 获取年龄
 * 月份按照正常的计算，1-12
 */
fun getAge(year: Int, month: Int, day: Int): Int {
    val currentTime = Calendar.getInstance()
    val yearNow = currentTime.get(Calendar.YEAR)
    val monthNow = currentTime.get(Calendar.MONTH) + 1
    val dayOfMonthNow = currentTime.get(Calendar.DAY_OF_MONTH)
    var age = yearNow - year
    when {
        month == monthNow -> {
            //月份相同去判断日
            if (day >= dayOfMonthNow) {
                age--
            }
        }
        month > monthNow -> {
            age--
        }
    }
    return abs(age)
}

/**
 * 深度拷贝
 */
fun <T : Serializable> T.copyDeep(): T {
    val startTime = System.currentTimeMillis()
    val baos = ByteArrayOutputStream()
    val out = ObjectOutputStream(baos)
    out.writeObject(this)
    out.close()
    val ins = ObjectInputStream(ByteArrayInputStream(baos.toByteArray()))
    val newEvent = ins.readObject()
    ins.close()
    val end = System.currentTimeMillis()
    logI("深度拷贝\"${javaClass.simpleName}\"花费时间：${end - startTime}")
    return newEvent as T
}

fun String.md5(): String {
    try {
        //获取md5加密对象
        val instance: MessageDigest = MessageDigest.getInstance("MD5")
        //对字符串加密，返回字节数组
        val digest: ByteArray = instance.digest(this.toByteArray())
        var sb: StringBuffer = StringBuffer()
        for (b in digest) {
            //获取低八位有效值
            var i: Int = b.toInt() and 0xff
            //将整数转化为16进制
            var hexString = Integer.toHexString(i)
            if (hexString.length < 2) {
                //如果是一位的话，补0
                hexString = "0" + hexString
            }
            sb.append(hexString)
        }
        return sb.toString()

    } catch (e: NoSuchAlgorithmException) {
        e.printStackTrace()
    }
    return ""
}

/**
 * aes解密
 */
fun String.aesDecrypt(key: String): String {
    return try {
        val secretKeySpec = SecretKeySpec(key.toByteArray(), "AES")
        val instance = Cipher.getInstance("AES/ECB/PKCS7Padding")
        instance.init(2, secretKeySpec)
        String(instance.doFinal(Base64.decode(this, 0))).trim()
    } catch (e: java.lang.Exception) {
        e.printStackTrace()
        this
    }
}

/**
 * aes加密
 */
fun String.aesEncrypt(key: String): String {
    return try {
        val secretKeySpec = SecretKeySpec(key.toByteArray(), "AES")
        val instance: Cipher = Cipher.getInstance("AES/ECB/PKCS7Padding")
        instance.init(1, secretKeySpec)
        Base64.encodeToString(instance.doFinal(this.toByteArray()), 0).trim()
    } catch (e: Exception) {
        e.printStackTrace()
        this
    }
}

/**
 * 修改动画时长缩放数值
 * 防止作弊
 * android10 无法访问
 */
fun ViewPropertyAnimator?.disEnableSystemAnimDurationScale() {
    this ?: return
    ignoreError {
        val mAnimatorMap = ViewPropertyAnimator::class.java.getDeclaredField("mAnimatorMap")
        mAnimatorMap.isAccessible = true
        mAnimatorMap.get(this).safeGet<HashMap<Animator, *>>()?.keys?.forEach {
            if (it is ValueAnimator) {
                disEnableSystemAnimDurationScale(it)
            }
        }
    }
}

/**
 * 修改动画时长缩放数值
 * 防止作弊
 * android10 无法访问
 */
fun disEnableSystemAnimDurationScale(anim: ValueAnimator?) {
    val cls = ValueAnimator::class.java
    if (Build.VERSION.SDK_INT < Build.VERSION_CODES.Q) {
        ignoreError {
            val field = cls.getDeclaredField("sDurationScale")
            field.isAccessible = true
            field.set(null, 1.0f)
        }
    } else {
        if (anim != null) {
            ignoreError {
                val field = cls.getDeclaredField("mDurationScale")
                field.isAccessible = true
                field.set(anim, 1.0f)
            }
        }
    }
}